﻿using GameProject.Filters;
using GameProject.Models;
using GameProject.Service;
using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WebMatrix.WebData;

namespace GameProject.Controllers
{
	public class Connect4GameHub : Hub
	{
        /// <summary>
        /// Sends message from the player with the playerId to everybody in the group
        /// and calls the addmessage function
        /// </summary>
        /// <param name="message"></param>
        /// <param name="groupId"></param>
        /// <param name="playerId"></param>
		public void Send(string message, string groupId, int playerId)
        {
            GameInstance game = gameService.GetGameById(int.Parse(groupId));
            string userName = Context.User.Identity.Name;

            // Call the addMessage method on all clients
            Clients.Group(groupId).addMessage(userName, message);
        }
        
        private GameInstanceService gameService = new GameInstanceService();

        /// <summary>
        /// Takes the id of the column that was clicked and finds an empty cell
        /// in that column. Saves the status of the cell to the database.
        /// Uses the functions isFullColumn, isFullGrid, setPiece, HasWon and tellWhosTurn. 
        /// </summary>
        /// <param name="gameId"></param>
        /// <param name="playerId"></param>
        /// <param name="clicked_id"></param>
		public void setPiece(int gameId, int playerId, string clicked_id)
		{
			GameInstance game = gameService.GetGameById(gameId);

            //Get the cells of the grid for the game. Typecast from ICollection to List. 
            List<GridCell> cells = game.Grid.Cells.ToList();

            //Sets the right column by the id of the arrow that was clicked.
			int column = 0;
			if (clicked_id.Equals("first"))
			{
				column = 1;
			}
			else if (clicked_id.Equals("second"))
			{
				column = 2;
			}
            else if (clicked_id.Equals("third"))
            {
                column = 3;
            }
            else if (clicked_id.Equals("fourth"))
            {
                column = 4;
            }
            else if (clicked_id.Equals("fifth"))
            {
                column = 5;
            }
            else if (clicked_id.Equals("sixth"))
            {
                column = 6;
            }
            else if (clicked_id.Equals("seventh"))
            {
                column = 7;
            }

            if (isFullColumn(column, cells))
            {
                Clients.Group(gameId.ToString()).tellWhosTurn(playerId, playerId);
            }
            else
            {
                //Check the cells from bottom up by setting i = 5.
                int index = 0;
                for (int i = 5; i >= 0; i--)
                {
                    //i * 7 + column gets the right index for the cell according to the GridCell table.
                    index = (i * 7) + column;

                    if (cells[index - 1].status == 0)
                    {
                        //Set the status of the cell to 1 or 2 (decided by the playerId)
                        cells[index - 1].status = playerId;

                        //Save changes to database
                        gameService.Save();

                        break;
                    }
                }

                //Call the function putPiece in the script file that puts yellow or ret <span>
                //in the appropriate cell
                Clients.Group(gameId.ToString()).putPiece(clicked_id, playerId, index);

                //Calls HasWon witch checks if the player has won
                if (HasWon(playerId, gameId, cells))
                {
                    Clients.Group(gameId.ToString()).sendWinner(playerId);
                    game.Status = GameStatus.DONE;
                }
                else if (isFullGrid(cells))
                {
                    Clients.Group(gameId.ToString()).noWinner();
                }
                else
                {
                    int nextPlayerId;
                    if (playerId == 1)
                    {
                        nextPlayerId = 2;
                    }
                    else
                    {
                        nextPlayerId = 1;
                    }

                    //Calls tellWhosTurn on the client side whitch tells the players whos turn it is
                    Clients.Group(gameId.ToString()).tellWhosTurn(playerId, nextPlayerId);
                }
            }
		}

        /// <summary>
        /// Function that adds a player to a group and then the game with the help of SignalR
        /// </summary>
        /// <param name="groupId">The ID of the game to be played</param>
		public void Join(string groupId)
		{
			if (!string.IsNullOrEmpty(groupId))
			{

				//Get the game
				GameInstance game = gameService.GetGameById(int.Parse(groupId));

				if (game.CurrentNumberOfPlayers < 2)
				{
					//Add player to group
					Groups.Add(Context.ConnectionId, groupId);
					game.CurrentNumberOfPlayers += 1;

					gameService.Save();

					//Find out what user is logged in
					int currentUserId = WebSecurity.CurrentUserId;
					UserProfile userProfile = gameService.GetUserById(currentUserId);

                    //Sets CurrentNumbeOfPlayers = ConnectionId of the user joining the game
					Clients.Client(Context.ConnectionId).sendUserId(game.CurrentNumberOfPlayers);

					//If enough players, start the game
					if (game.CurrentNumberOfPlayers == 2)
					{
						// initializeGrid();
						game.Status = GameStatus.PLAYING;
						gameService.Save();
						Clients.Group(groupId).startGame(game.ExpectedNumberOfPlayers);
					}
				}
			}
		}

        /// <summary>
        /// Checks if a player with the playerId has won the game
        /// </summary>
        /// <param name="playerId"></param>
        /// <param name="gameId"></param>
        /// <param name="cells"></param>
        /// <returns></returns>
        public bool HasWon(int playerId, int gameId, List<GridCell> cells)
        {
            bool check = false;
            int mult = 1;
            //Horizontal check for four in a row (of either 1 or 2 depending on the playerId)
            //The variable mult checks if the multiplicity of the status of four adjacent cells
            //is either 1(for player1, 1*1*1*1) or 16(for player2, 2*2*2*2)
            for (int i = 5; i >= 0; i--)
            {
                for (int j = 0; j < 4; j++)
                {
                    for (int k = 0; k < 4; k++)
                    {
                        mult = mult * cells[i * 7 + j + k].status;
                    }
                    if(mult == (playerId * playerId * playerId * playerId))
                    {
                        check = true;
                        break;
                    }
                    mult = 1;
                }
            }
            //Vertical check for four in a row
            for (int i = 2; i >= 0; i--)
            {
                for (int j = 0; j <= 6; j++)
                {
                    for (int k = 0; k < 4; k++)
                    {
                        mult = mult * cells[i * 7 + k * 7 + j].status;
                    }
                    if (mult == (playerId * playerId * playerId * playerId))
                    {
                        check = true;
                        break;
                    }
                    mult = 1;
                }
            }
            //Diagonal check for four in a row (\)
            for (int i = 2; i >= 0; i--)
            {
                for (int j = 0; j <= 3; j++)
                {
                    for (int k = 0; k <= 3; k++)
                    {
                        mult = mult * cells[i * 7 + k * 8 + j].status;
                    }
                    if (mult == (playerId * playerId * playerId * playerId))
                    {
                        check = true;
                        break;
                    }
                    mult = 1;
                }
            }
            //Diagonal check for four in a row (/)
            for (int i = 2; i >= 0; i--)
            {
                for (int j = 0; j <= 3; j++)
                {
                    for (int k = 1; k <= 4; k++)
                    {
                        mult = mult * cells[i * 7 + k * 6 - j].status;
                    }
                    if (mult == (playerId * playerId * playerId * playerId))
                    {
                        check = true;
                        break;
                    }
                    mult = 1;
                }
            }
            

            return check;
        }

        /// <summary>
        /// Checks if the column that was clicked is full
        /// </summary>
        /// <param name="columnNum"></param>
        /// <param name="cells"></param>
        /// <returns></returns>
        public bool isFullColumn(int columnNum, List<GridCell> cells)
        {
            return cells[columnNum - 1].status != 0;
        }

        /// <summary>
        /// Checks if the grid is full
        /// </summary>
        /// <param name="cells"></param>
        /// <returns></returns>
        public bool isFullGrid(List<GridCell> cells)
        {
            int test = 1;
            for (int i = 0; i < 7; i++)
            {
                test *= cells[i].status;
            }
            return test != 0;
        }

	}

}